module ov2640(
    input clk,
    input rst_n,
    input dcmi_start,
    input dcmi_busy,

    input dcmi_vsync,
    input dcmi_hsync,
    input dcmi_pclk,
    input [7:0] dcmi_data,

    output [31:0] test_data,
    input [15:0] test_addr,
    output sclk,

    output mem_valid,
    output [3:0] mem_wstrb,
    output [31:0] mem_addr,
    output [31:0] mem_wdata,
    input [31:0] mem_rdata,
    input mem_ready,

    output [31:0] byte_count
);

reg mem_valid_r;
assign mem_valid = mem_valid_r;
reg [3:0] mem_wstrb_r;
assign mem_wstrb = mem_wstrb_r;
reg [31:0] mem_addr_r;
assign mem_addr = mem_addr_r;
reg [31:0] mem_wdata_r;
assign mem_wdata = mem_wdata_r;



//async clk signal handle
always @(posedge clk ) begin

    if(dcmi_pclk_start == dcmi_clk_start)begin
        dcmi_clk_start <= dcmi_start;
    end
    else begin
        dcmi_clk_start <= dcmi_clk_start;
    end
    
end
reg dcmi_pclk_start;
always @(posedge dcmi_pclk ) begin
    dcmi_pclk_start <= dcmi_clk_start;
end


wire        sof_detected;  // Start of Frame detected
wire [15:0] marker_check; // 用于检测JPEG标记
reg [7:0] marker_check_reg0;
assign marker_check = {marker_check_reg0[7:0],dcmi_data[7:0]};
assign sof_detected = (marker_check == 16'hFFD8);

// 检测JPEG标记 (SOI, EOI等)
always @(posedge dcmi_pclk or negedge rst_n) begin
    if (!rst_n) begin
        marker_check_reg0 <= 8'h00;
    end else begin
        marker_check_reg0 <= dcmi_data;
    end
end

reg dcmi_busy_r;
assign dcmi_busy = dcmi_busy_r;
always @(posedge dcmi_pclk ) begin
    if(!rst_n) begin
        dcmi_busy_r <= 'b0;
    end
    else if(dcmi_pclk_start) begin
        dcmi_busy_r <= 'b1;
    end
    else if(dcmi_state == dcmi_idle) begin
        dcmi_busy_r <= 'b0;
    end
    else begin
        dcmi_busy_r <= dcmi_busy_r;
    end
end 

reg [7:0] dcmi_data_delay0;
reg [7:0] dcmi_data_delay1;
reg dcmi_vsync_r0;
reg dcmi_vsync_r1;
reg dcmi_hsync_r0;
reg dcmi_hsync_r1;
reg dcmi_clk_start;

wire [15:0] eoi_data;
assign eoi_data = {dcmi_data_delay1[7:0],dcmi_data_delay0[7:0]};
wire        eoi_detected;  // End of Image detected
assign eoi_detected = (eoi_data == 16'hFFD9);


always @(posedge dcmi_pclk or negedge rst_n) begin
    if(!rst_n) begin
        dcmi_data_delay0 <= 'h00;
        dcmi_data_delay1 <= 'h00;
        dcmi_hsync_r0 <= 'b0;
        dcmi_hsync_r1 <= 'b0;
        dcmi_vsync_r0 <= 'b0;
        dcmi_vsync_r1 <= 'b0;
    end
    else begin
        dcmi_hsync_r0 <= dcmi_hsync;
        dcmi_hsync_r1 <= dcmi_hsync_r0;

        dcmi_vsync_r0 <= dcmi_vsync;
        dcmi_vsync_r1 <= dcmi_vsync_r0;
        dcmi_data_delay0 <= dcmi_data;
        dcmi_data_delay1 <= dcmi_data_delay0;
    end
end

localparam dcmi_idle        =   'd0;
localparam dcmi_wait_data   =   'd1;
localparam dcmi_data_recv   =   'd2;
localparam dcmi_data_end0   =   'd3;
localparam dcmi_data_end1   =   'd4;

reg [2:0] dcmi_state;
reg [7:0] fifo_wdata;
wire fifo_full;
reg fifo_wr_en;
reg [31:0] byte_count_r;
assign byte_count = byte_count_r;

reg eoi_detected_r0;
reg eoi_detected_r1;
assign sclk = dcmi_pclk;
always @(posedge dcmi_pclk or negedge rst_n) begin
    if(!rst_n) begin
        dcmi_state <= dcmi_idle;
        fifo_wdata <= 'd0;
        fifo_wr_en <= 'b0;
        byte_count_r <= 'd0;
    end
    else begin
        dcmi_state <= dcmi_state;
        fifo_wr_en <= 'b0;
        byte_count_r <= byte_count_r;
        eoi_detected_r0 <= eoi_detected;
        eoi_detected_r1 <= eoi_detected_r0;
        case(dcmi_state)
        dcmi_idle:begin
            fifo_wr_en <= 'b0;
            eoi_detected_r0 <= 'b0;
            eoi_detected_r1 <= 'b0;
            if(dcmi_pclk_start)begin
                dcmi_state <= dcmi_wait_data;
                byte_count_r <= 'd0;
            end
        end
        dcmi_wait_data:begin
            if(sof_detected)begin
                dcmi_state <= dcmi_data_recv;
            end
        end
        dcmi_data_recv:begin
            fifo_wr_en <= 'b0;
            dcmi_state <= dcmi_state;
            if(eoi_detected_r1)begin
                dcmi_state <= dcmi_data_end0;
            end
            else begin
                if( (dcmi_vsync_r1 == 'b1) && (dcmi_hsync_r1 == 'b1))begin
                fifo_wr_en <= 'b1;
                fifo_wdata <= dcmi_data_delay1;
                byte_count_r <= byte_count_r + 'b1;
                end
                else begin
                    fifo_wr_en <= 'b0;
                end
                dcmi_state <= dcmi_state;
            end
            
        end
        dcmi_data_end0:begin
            fifo_wr_en <= 'b1;
            fifo_wdata <= 'hff;
            dcmi_state <= dcmi_data_end1;
            byte_count_r <= byte_count_r + 'b1;
        end
        dcmi_data_end1:begin
            fifo_wr_en <= 'b1;
            fifo_wdata <= 'hd9;
            dcmi_state <= dcmi_idle;
            byte_count_r <= byte_count_r + 'b1;
        end
        endcase
    end
end
reg mem_access_req;
reg mem_access_req_r;
wire clk_mem_access;
reg mem_finish_sig;

clka_to_clkb clka_to_clkb_u0(
    .clka(dcmi_pclk),
    .clkb(clk),
    .clka_in(mem_access_req),
    .clkb_out(clk_mem_access)
);

localparam write_idle   =   'd0;
localparam write_wait    =   'd1;

reg [1:0] write_state;
reg [31:0] write_byte_cnt;

reg [31:0] write_count;
// assign test_data = write_count;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        mem_valid_r <= 'b0;
        mem_wstrb_r <= 'b0000;
        mem_addr_r <= 'h01_00_00_00;
        write_state <= write_idle;
        mem_finish_sig <= 'b0;
    end
    else begin
        mem_valid_r <= mem_valid_r;
        mem_wstrb_r <= mem_wstrb_r;
        write_state <= write_state;
        mem_addr_r <= mem_addr_r;
        mem_access_req_r <= mem_access_req;
        mem_finish_sig <= mem_finish_sig;
        if(dcmi_clk_start) begin
            mem_addr_r <= 'h01_00_00_00;
            write_count <= 'd0;
        end
        case(write_state)
        write_idle:begin
            mem_valid_r <= 'b0;
            mem_wstrb_r <= 'b0000;
            if((clk_mem_access == 'b1)&&(mem_finish_sig == 'b0))begin
                mem_wdata_r <= fifo_data32;
                write_state <= write_wait;
                write_count <= write_count + 'b1;
            end
            else begin
                mem_finish_sig <= clk_mem_access&mem_finish_sig;
            end
        end
        write_wait:begin
            mem_valid_r <= 'b1;
            mem_wstrb_r <= 'b1111;
            mem_wdata_r <= fifo_data32;
            if(mem_valid_r&&mem_ready)begin
                mem_finish_sig <= 'b1;
                mem_addr_r <= mem_addr_r + 'd4;
                mem_valid_r <= 'b0;
                mem_wstrb_r <= 'b0000;
                write_state <= write_idle;
            end
        end
        endcase
        
    end
end


reg fifo_rd_en;
wire fifo_empty;
reg fifo_flag;

reg mem_finish_sig_r0;
wire dcmi_clk_mem_finish_sig;
assign mem_finish_sig_pos = (~mem_finish_sig_r0)&&(mem_finish_sig);

clka_to_clkb clka_to_clkb_u1(
    .clka(clk),
    .clkb(dcmi_pclk),
    .clka_in(mem_finish_sig),
    .clkb_out(dcmi_clk_mem_finish_sig)
);

wire [7:0] fifo_dout;
reg [31:0] fifo_data32;
reg [3:0] fifo_data_flag;

reg [31:0] dcmi_pclk_write_count;
assign test_data = dcmi_pclk_write_count;

always @(posedge dcmi_pclk or negedge rst_n) begin
    if(!rst_n)begin
        mem_access_req <= 'b0;
        fifo_rd_en <= 'b0;
        fifo_flag <= 'b0;
        fifo_data_flag <= 'b0;
        dcmi_pclk_write_count <= 'd0;
    end
    else begin
        mem_finish_sig_r0 <= mem_finish_sig;
        fifo_flag <= fifo_flag;
        fifo_rd_en <= 'b0;
        dcmi_pclk_write_count <= dcmi_clk_start ? 'd0 : dcmi_pclk_write_count;
        fifo_data32 <= fifo_rd_en ? {fifo_dout[7:0],fifo_data32[31:8]} : fifo_data32;
        if(fifo_flag == 'b0) begin
            mem_access_req <= 'b0;
            if((fifo_empty == 'b0)&&(fifo_data_flag != 'b1111)&&(mem_access_req == 'b0))begin
                fifo_rd_en <= 'b1;
                fifo_data_flag <= {'b1,fifo_data_flag[3:1]};
                
            end
            else begin
                fifo_flag <= fifo_flag;
                fifo_data_flag <= fifo_data_flag;
                if((mem_access_req == 'b0)&&(dcmi_clk_mem_finish_sig == 'b0))begin//first mem ready
                    if(fifo_data_flag == 'b1111) begin//byte full read mem write
                        fifo_flag <= 'b1;
                        // fifo_data32 <= fifo_data32;
                    end
                    //byte don't full but fifo is empty and dcmi don't have data
                    else if((fifo_data_flag != 'b0000)&&(fifo_empty == 'b1)&&(dcmi_state == dcmi_idle)) begin
                        fifo_flag <= 'b1;
                        case(fifo_data_flag)
                        'b1000:begin
                            fifo_data32 <= {24'h0,fifo_data32[31:24]};
                        end
                        'b1100:begin
                            fifo_data32 <= {16'h0,fifo_data32[31:16]};
                        end
                        'b1110:begin
                            fifo_data32 <= {8'h0,fifo_data32[31:8]};
                        end
                        endcase
                    end
                end
            end
        end
        else begin
            fifo_rd_en <= 'b0;
            mem_access_req <= 'b1;
            fifo_data32 <= fifo_data32;
            if(dcmi_clk_mem_finish_sig)begin
                dcmi_pclk_write_count <= dcmi_pclk_write_count + 'b1;
                fifo_data_flag <= 'b0000;
                fifo_flag <= 'b0;
                mem_access_req <= 'b0;
            end
        end
    end
end
// 实例化FIFO用于JPEG数据缓冲
jpeg_fifo #(
    .DATA_WIDTH(8),
    .FIFO_DEPTH(2048)
) u_jpeg_fifo (
    .clk(dcmi_pclk),
    .rst_n(rst_n),
    .wr_en(fifo_wr_en),
    .wr_data(fifo_wdata),
    .full(fifo_full),
    .rd_en(fifo_rd_en),
    .rd_data(fifo_dout),
    .empty(fifo_empty),
    .data_count()
);

endmodule
// FIFO模块
module jpeg_fifo #(
    parameter DATA_WIDTH = 8,
    parameter FIFO_DEPTH = 1024
)(
    input wire clk,
    input wire rst_n,
    input wire wr_en,
    input wire [DATA_WIDTH-1:0] wr_data,
    output wire full,
    input wire rd_en,
    output wire [DATA_WIDTH-1:0] rd_data,
    input [15:0] rdata_addr,
    output wire empty,
    output wire [15:0] data_count
);

reg [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
reg [15:0] wr_ptr = 0;
reg [15:0] rd_ptr = 0;
reg [15:0] count = 0;

assign full = (count == FIFO_DEPTH);
assign empty = (count == 0);
assign rd_data = mem[rd_ptr];
assign data_count = count[15:0];

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wr_ptr <= 0;
        rd_ptr <= 0;
        count <= 0;
    end else begin
        // 写操作
        if (wr_en && !full) begin
            mem[wr_ptr] <= wr_data;
            wr_ptr <= (wr_ptr == FIFO_DEPTH-1) ? 0 : wr_ptr + 1;
        end
        
        // 读操作
        if (rd_en && !empty) begin
            rd_ptr <= (rd_ptr == FIFO_DEPTH-1) ? 0 : rd_ptr + 1;
        end
        
        // 更新计数器
        case ({wr_en && !full, rd_en && !empty})
            2'b01: count <= count - 1;
            2'b10: count <= count + 1;
            2'b11: count <= count; // 同时读写，计数不变
            default: count <= count;
        endcase
    end
end

endmodule